package com.mofum.msdom.excel.template.writer;

import com.mofum.msdom.excel.builder.StringCellBuilder;
import com.mofum.msdom.excel.builder.StyleBuilder;
import com.mofum.msdom.excel.constant.ExcelValue;
import com.mofum.msdom.excel.constant.SheetItemType;
import com.mofum.msdom.excel.hander.poi.AnnotationContentHandler;
import com.mofum.msdom.excel.metadata.MPSheet;
import com.mofum.msdom.excel.metadata.MPStyle;
import com.mofum.msdom.excel.template.event.TemplateCallback;
import com.mofum.msdom.excel.template.hander.poi.TemplateCellReadHandler;
import com.mofum.msdom.excel.template.metadata.MPExcelTemplate;
import com.mofum.msdom.excel.template.metadata.MPSheetTemplateItem;
import com.mofum.msdom.excel.template.writer.impl.ExcelTemplateWriterImpl;
import org.apache.poi.ooxml.util.SAXHelper;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.StylesTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;

/**
 * @author yuyang@qxy37.com
 * @since 2019/2/1 0001 14:31
 */
public abstract class AbstractExcelTemplateWriter implements IExcelTemplateWriter {

    public static Logger logger = LoggerFactory.getLogger(AbstractExcelTemplateWriter.class);
    /**
     * 模板
     */
    protected MPExcelTemplate template;

    /**
     * Excel
     */
    protected Workbook excel;

    /**
     * 工作薄类型   属性名 - 类型
     */
    protected Map<String, SheetItemType> sheetTypes;

    /**
     * 模板选项
     */
    protected Map<String, MPSheetTemplateItem> sheetTemplateItemMap;

    /**
     * 模板数据
     */
    protected Map<String, ExcelValue> templateData;


    protected void replaceValue() {

        if (excel != null && sheetTemplateItemMap != null) {

            Set<String> set = sheetTemplateItemMap.keySet();

            for (String propName : set) {

                MPSheetTemplateItem item = sheetTemplateItemMap.get(propName);

                ExcelValue excelValue = templateData.get(propName);

                if (excelValue == null) {
                    continue;
                }

                Sheet sheet = excel.getSheetAt(item.getSheetIndex());

                if (sheet == null) {
                    sheet = excel.createSheet();
                }

                StringCellBuilder cellBuilder = new StringCellBuilder(sheet.getRow(item.getRowIndex()));
                cellBuilder.load(item.getColIndex()).build();

                MPStyle style = null;
                if (item.getStyles() != null && item.getStyles().length > 0) {
                    style = item.getStyles()[0];
                }

                if (style != null) {
                    StyleBuilder styleBuilder = new StyleBuilder(excel);
                    if (excelValue.getDataFormat() != null && "".equals(excelValue.getDataFormat())) {
                        styleBuilder.dataFormat(excelValue.getDataFormat());
                    }
                    CellStyle cellStyle = styleBuilder.mpStyle(style).build();
                    cellBuilder.style(cellStyle);
                }

            }

        }
    }

    public MPExcelTemplate getTemplate() {
        return template;
    }

    public void setTemplate(MPExcelTemplate template) {
        this.template = template;
    }

    public Workbook getExcel() {
        return excel;
    }

    public void setExcel(Workbook excel) {
        this.excel = excel;
    }

    public Map<String, SheetItemType> getSheetTypes() {
        return sheetTypes;
    }

    public void setSheetTypes(Map<String, SheetItemType> sheetTypes) {
        this.sheetTypes = sheetTypes;
    }

    /**
     * 模板读取器
     */
    private class TemplateReader {

        /**
         * Workbook 读取地址
         */
        private String address;

        /**
         * 包
         */
        private OPCPackage opcPackage;

        /**
         * 模板单元格读取器
         */
        private TemplateCellReadHandler readHandler;

        /**
         * 值读取回调
         */
        private TemplateCallback valueCallback;

        /**
         * 属性读取回调
         */
        private TemplateCallback propCallback;

        /**
         * 工作薄数据读取前的初始化内容
         */
        private SheetInitCallback sheetInitCallback;

        public TemplateReader(String address) {
            this.address = address;

            try {
                opcPackage = OPCPackage.open(address);
            } catch (InvalidFormatException e) {
                logger.error(e.getMessage(), e);
            }

            if (readHandler == null) {
                readHandler = new TemplateCellReadHandler();
            }
            load();
        }

        public TemplateReader(String address, TemplateCallback valueCallback, TemplateCallback propCallback) {
            this.address = address;

            try {
                opcPackage = OPCPackage.open(address);
            } catch (InvalidFormatException e) {
                logger.error(e.getMessage(), e);
            }

            if (readHandler == null) {
                readHandler = new TemplateCellReadHandler();
            }

            this.valueCallback = valueCallback;

            this.propCallback = propCallback;

            readHandler.setTemplatePropCallback(propCallback);

            readHandler.setValuePropCallback(valueCallback);

            load();
        }

        public void load() {

            try {
                XSSFReader xssfReader = new XSSFReader(opcPackage);

                StylesTable styles = xssfReader.getStylesTable();

                ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(opcPackage);

                XSSFReader.SheetIterator sheetsData = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
                int sheetIndex = 0;
                while (sheetsData.hasNext()) {

                    InputStream sheetInputStream = null;

                    try {
                        sheetInputStream = sheetsData.next();

                        if (sheetInitCallback != null) {
                            sheetInitCallback.init(sheetIndex);
                        }

                        processSheet(styles, strings, sheetInputStream, readHandler);

                        sheetIndex++;

                    } finally {
                        if (sheetInputStream != null) {
                            sheetInputStream.close();
                        }
                    }
                }

            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            } catch (SAXException e) {
                logger.error(e.getMessage(), e);
            } catch (InvalidFormatException e) {
                logger.error(e.getMessage(), e);
            } catch (OpenXML4JException e) {
                logger.error(e.getMessage(), e);
            } catch (ParserConfigurationException e) {
                logger.error(e.getMessage(), e);
            }
        }

        private void processSheet(StylesTable styles, ReadOnlySharedStringsTable strings, InputStream sheetInputStream, TemplateCellReadHandler handler)
                throws SAXException, ParserConfigurationException, IOException {
            XMLReader sheetParser = SAXHelper.newXMLReader();

            if (handler != null) {
                sheetParser.setContentHandler(new XSSFSheetXMLHandler(styles, strings, handler, false));
            }

            sheetParser.parse(new InputSource(sheetInputStream));

        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public OPCPackage getOpcPackage() {
            return opcPackage;
        }

        public void setOpcPackage(OPCPackage opcPackage) {
            this.opcPackage = opcPackage;
        }

        public TemplateCellReadHandler getReadHandler() {
            return readHandler;
        }

        public void setReadHandler(TemplateCellReadHandler readHandler) {
            this.readHandler = readHandler;
        }

        public TemplateCallback getValueCallback() {
            return valueCallback;
        }

        public void setValueCallback(TemplateCallback valueCallback) {
            this.valueCallback = valueCallback;
        }

        public TemplateCallback getPropCallback() {
            return propCallback;
        }

        public void setPropCallback(TemplateCallback propCallback) {
            this.propCallback = propCallback;
        }

        public SheetInitCallback getSheetInitCallback() {
            return sheetInitCallback;
        }

        public void setSheetInitCallback(SheetInitCallback sheetInitCallback) {
            this.sheetInitCallback = sheetInitCallback;
        }
    }

    private interface SheetInitCallback {
        void init(int sheetIndex);
    }
}
